package org.mikrosopht.client;

import com.google.gwt.user.client.Window;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.gen2.table.client.MutableTableModel;
import com.google.gwt.gen2.table.client.TableModel.Callback;
import com.google.gwt.gen2.table.client.TableModelHelper;
import com.google.gwt.gen2.table.client.TableModelHelper.SerializableResponse;

import java.util.ArrayList;

public class MovieDataSourceTableModel extends MutableTableModel<Movie> {
  private String currentSearch = "";
  private int nameColumnIndex = -1;
  private int reviewScoreColumnIndex = -1;
  private int tallyColumnIndex = -1;
  private int dateAddedColumnIndex = -1;
  private boolean showScreeningsOnly = false;
  private MovieListSort sort1 = null;
  private MovieListSort sort2 = null;
  private Request savedRequest = null;

  public void setNameColumnIndex(int index) {
    nameColumnIndex = index;
  }

  public void setReviewScoreColumnIndex(int index) {
    reviewScoreColumnIndex = index;
  }

  public void setTallyColumnIndex(int index) {
    tallyColumnIndex = index;
  }

  public void setDateAddedColumnIndex(int index) {
    dateAddedColumnIndex = index;
  }

  public void setShowScreeningsOnly(boolean value) {
    showScreeningsOnly = value;
  }

  public boolean getShowScreeningsOnly() {
    return showScreeningsOnly;
  }

  public void setSorting(MovieListSort sort1, MovieListSort sort2) {
    this.sort1 = sort1;
    this.sort2 = sort2;
    if (sort1 == MovieListSort.SCREENING_DATE_DESCENDING) {
      setShowScreeningsOnly(true);
    }
  }

  public MovieListSort getSort1() {
    return sort1;
  }

  private String getSortRequest(TableModelHelper.Request request) {
    if (showScreeningsOnly) {
      return "&sort1=" + MovieListSort.SCREENING_DATE_DESCENDING + "&sort2=" + MovieListSort.NAME_ASCENDING;
    }
    int sortColumnIndex = request.getColumnSortList().getPrimaryColumn();
    if (sortColumnIndex != -1) {
      boolean ascending = request.getColumnSortList().getPrimaryColumnSortInfo().isAscending();
      if (sortColumnIndex == nameColumnIndex) {
        if (ascending) {
          sort1 = MovieListSort.NAME_ASCENDING;
        } else {
          sort1 = MovieListSort.NAME_DESCENDING;
        }
        sort2 = MovieListSort.TALLY_DESCENDING;
      } else if (sortColumnIndex == reviewScoreColumnIndex) {
        if (ascending) {
          sort1 = MovieListSort.REVIEW_SCORE_ASCENDING;
        } else {
          sort1 = MovieListSort.REVIEW_SCORE_DESCENDING;
        }
        sort2 = MovieListSort.NAME_ASCENDING;
      } else if (sortColumnIndex == tallyColumnIndex) {
        if (ascending) {
          sort1 = MovieListSort.TALLY_ASCENDING;
        } else {
          sort1 = MovieListSort.TALLY_DESCENDING;
        }
        sort2 = MovieListSort.NAME_ASCENDING;
      } else if (sortColumnIndex == dateAddedColumnIndex) {
        if (ascending) {
          sort1 = MovieListSort.DATE_ADDED_ASCENDING;
        } else {
          sort1 = MovieListSort.DATE_ADDED_DESCENDING;
        }
        sort2 = MovieListSort.NAME_ASCENDING;
      }
    }
    if (sort1 != null && sort2 != null) {
      return "&sort1=" + sort1 + "&sort2=" + sort2;
    }
    return "&sort1=" + MovieListSort.TALLY_DESCENDING + "&sort2=" + MovieListSort.NAME_ASCENDING;
  }

  @Override
  public void requestRows(final TableModelHelper.Request request, final Callback<Movie> callback) {
    String sort = getSortRequest(request);
    String url = URL.encode(MovieVoteConstants.MOVIE_LIST_JSON_URL + "?count=" + request.getNumRows() + "&start=" + request.getStartRow() +
                            sort + "&namelike=" + currentSearch);
    RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
    try {
      savedRequest = builder.sendRequest(null, new RequestCallback() {
        public void onError(Request httpRequest, Throwable exception) {
        }
        public void onResponseReceived(Request httpRequest, Response response) {
          if (httpRequest != savedRequest) {
            return;
          }
          if (response.getStatusCode() != 200) {
            return;
          }
          ArrayList<Movie> movies = new ArrayList<Movie>();
          JSONObject object = JSONParser.parse(response.getText()).isObject();
          int movieCount = Integer.parseInt(object.get("count").isString().stringValue());
          setRowCount(movieCount);
          JSONArray array = object.get("movies").isArray();
          for (int i = 0; i < array.size(); i++) {
            movies.add(parseJsonToMovie(array.get(i).isObject()));
          }
          SerializableResponse<Movie> rowResponse = new SerializableResponse<Movie>(movies);
          callback.onRowsReady(request, rowResponse);
        }
      });
    } catch (RequestException e) {
      Window.alert("Couldn't retrieve JSON: " + e);
    }
  }

  public static Movie parseJsonToMovie(JSONObject movieObject) {
    String movieName = movieObject.get("name").isString().stringValue();
    String imdbUrl = movieObject.get("imdb_url").isString().stringValue();
    String rottenUrl = movieObject.get("rotten_url").isString().stringValue();
    String metacriticUrl = movieObject.get("metacritic_url").isString().stringValue();
    String trailerUrl = movieObject.get("youtube_trailer_url").isString().stringValue();
    String reviewScore = movieObject.get("review_average_score").isString().stringValue();
    String tally = movieObject.get("tally").isString().stringValue();
    String tallyDetail = movieObject.get("tally_detail").isString().stringValue();
    String dateAdded = movieObject.get("date_added").isString().stringValue();
    int minusVotes = Integer.parseInt(movieObject.get("downvoter_count").isString().stringValue());
    int plusVotes = Integer.parseInt(movieObject.get("voter_count").isString().stringValue());
    int screenings = Integer.parseInt(movieObject.get("screenings").isString().stringValue());
    int plusVoted = Integer.parseInt(movieObject.get("voted").isString().stringValue());
    int minusVoted = Integer.parseInt(movieObject.get("downvoted").isString().stringValue());
    int rating = Integer.parseInt(movieObject.get("rating").isString().stringValue());
    
    Movie movie = new Movie();
    movie.setName(movieName);
    movie.setMinusVotes(minusVotes);
    movie.setPlusVotes(plusVotes);
    movie.setScreenings(screenings);
    movie.setRating(rating);
    movie.setDateAdded(dateAdded);
    if (plusVoted > 0) {
      movie.setVote(1);
    } else if (minusVoted > 0) {
      movie.setVote(-1);
    }
    if (imdbUrl.length() > 0) {
      movie.setImdbUrl(imdbUrl);
    }
    if (rottenUrl.length() > 0) {
      movie.setRottenUrl(rottenUrl);
    }
    if (metacriticUrl.length() > 0) {
      movie.setMetacriticUrl(metacriticUrl);
    }
    if (trailerUrl.length() > 0) {
      movie.setTrailerUrl(trailerUrl);
    }
    if (tallyDetail.length() > 0) {
      movie.setTallyDetail(tallyDetail);
    }
    try {
      movie.setReviewScore(Float.parseFloat(reviewScore));
    } catch (NumberFormatException nfe) {
    }
    try {
      movie.setTally(Float.parseFloat(tally));
    } catch (NumberFormatException nfe) {
    }
    return movie;
  }

  @Override
  protected boolean onRowRemoved(int row) {
    return true;
  }

  @Override
  protected boolean onRowInserted(int beforeRow) {
    return true;
  }

  @Override
  protected boolean onSetRowValue(int row, Movie rowValue) {
    return true;
  }

  public void setSearch(String text) {
    currentSearch = text;
  }
}
